This is a reproduction of Beautiful plotting in R: A ggplot2 cheatsheet by Zev Ross. The motivation behind this exercise is to take advantage of the R Notebook format, allowing us to visualize the plotting results after each code chunk. Credits to the original author Zev Ross, first published on August 4, 2014.

Quick Setup: The dataset

We’re using data from the National Morbidity and Mortality Air Pollution Study (NMMAPS). To make the plots manageable we’re limiting the data to Chicago and 1997-2000. For more detail on this dataset, consult Roger Peng’s book Statistical Methods in Environmental Epidemiology with R.

# Load the required library 
library(ggplot2)
There were 36 warnings (use warnings() to see them)
# Load our dataset
nmmaps <- read.csv("chicago-nmmaps.csv", as.is = T)
# Take data only after 1/1/1997
nmmaps$date <- as.Date(nmmaps$date)
nmmaps <- nmmaps[nmmaps$date > as.Date("1996-12-31"),]
# Substring the first four elements [1:4] of the date char so ("1997-01-01") returns "1997"
nmmaps$year <- substring(nmmaps$date, 1,4)
head(nmmaps)

A default plot in ggplot2

g <- ggplot(nmmaps, aes(date, temp))+geom_point(color="firebrick")
g

Working with the title

Add a title: ggtitle() or labs()

g <- g + ggtitle('Temperature')
g

Alternatively, we can use labs(): g + labs(title = ‘Title’)

Make title bold and add a little space at the baseline: face, margin

Note that the margin argument uses the margin function and we provide the top, right, bottom, left margins in that order. The default unit is points.

g <- g + theme(plot.title = element_text(size = 20, face = "bold", margin = margin(10,0,10,0)))
g

Using a non-traditional font in your title: family

library(extrafont)
g <- g + theme(plot.title = element_text(size = 20, lineheight = .8, vjust=1, family = "Roboto Condensed"))
g

Change spacing in multi-line text: lineheight

g <- g + ggtitle("Temperature fluctuations \n between 1997 and 2001")
g <- g + theme(plot.title = element_text(size=20, face="bold", vjust=1, lineheight = .8))
g

Working with axes

Add x and y axis label: labs(), xlab()

g <- g+labs(x="Date", y=expression(paste("Temperature (", degree ~ F, " )")), title = "Temperature")
g

Get rid of axis ticks and tick text: theme(), axis.ticks.y

g + theme(axis.ticks.y = element_blank(), axis.text.y = element_blank())

Change size of and rotate tick text: axis.text.x

g + theme(axis.text.x = element_text(angle=50, size=12, vjust=0.5))

Move the labels away from the plot and add color: axis.title.x, vjust

g + theme(axis.title.x = element_text(color="forestgreen", vjust=0.35),
          axis.title.y = element_text(color="cadetblue", vjust=0.35))

Limit an axis to a range: ylim(), scale_x_continuous(), coord_cartesian()

g + ylim(c(0,60))

Alternatively: g + scale_x_continuous(limits=c(0,35)) + g+coord_cartesian(xlim=c(0,35)). The former removes all data points outside the range and second one adjusts the visible area.

If you want the axes to be the same: coord_equal()

For demo purposes, we will plot the temperature against the temperature with some random noise. We want both axes to be the same scale / same range.

# rnorm(1461, mean=0, sd=20) creates 1461 numbers between 0 to 20
ggplot(nmmaps, aes(temp, temp + rnorm(nrow(nmmaps), sd=20))) + geom_point(color="mediumpurple") +xlim(c(0,150))+ylim(c(0,150))+coord_equal()

Use a function to alter labels: label=function(x)

ggplot(nmmaps, aes(date, temp)) + geom_point(color="grey") + labs(x="Month", y="Temp") + scale_y_continuous(label=function(x){return (paste("My value is", x, "degrees"))})

Working with the legend

We will color code the plot based on season. We observe that by default the legend title is what we’ve specified in the color argument.

g <- ggplot(nmmaps, aes(date, temp, color=factor(season))) + geom_point()
g

Turning off the legend title

g + theme(legend.title=element_blank())

Change the title of the legend: scale_color_discrete(name=“Title”)

To change the title of the legend we would use the name argument in our scale function. Without the scale function, we will need to change the data itself so it has the right format.

g <- ggplot(nmmaps, aes(date, temp, color=factor(season))) + geom_point()
g + theme(legend.title = element_text(color="chocolate", size=10, face="bold")) + scale_color_discrete(name="Chocolate legend text")

Add a box around legend: legend.background

g + theme(legend.background = element_rect(fill="gray90", size=.8))

Change the box color in each legend key: legend.key(element_rect(fill=“red”))

g + theme(legend.key = element_rect(fill="lightgoldenrod2"))

Change the position of the legend: legend.position()

g + theme(legend.position = "top")

Change the size of the symbols in the legend keys

g + guides(color = guide_legend(override.aes = list(size=5)))

Leave a layer off the legend: show_guide

Let’s say we have a point layer and add label text to it using geom_text (now we have two layers), by default both the points and the label text layers (again, two layers) will end up in our legend like this:

g <- ggplot(nmmaps, aes(date, temp, color=factor(season))) + geom_point()
g + geom_text(data=nmmaps, aes(date, temp, label=round(temp)), size=3)

Fortunately, we can turn off a layer in the legend using show_guide=F

g + geom_text(data=nmmaps, aes(date, temp, label=round(temp)), size=3, show_guide=F)
`show_guide` has been deprecated. Please use `show.legend` instead.

Manually adding legend items

ggplot2 will not add a legend automatically unless we map aethetics (color, size etc) to a variable. There are times, though, that we want to have a legend so that it’s clear what you’re plotting. Here is the default:

# No legend by default because we didn't map aes to a variable
ggplot(nmmaps, aes(x=date, y=o3)) + geom_line(color="grey") + geom_point(color="red", size=.8)

We can force a legend by mapping to a “variable”. We are mapping the lines and the points using aes and we are mapping not to a variable in our dataset but to a single string (so that we get just one color for each).

ggplot(nmmaps, aes(x=date, y=o3)) + geom_line(aes(color="Important lines")) + geom_point(aes(color="My points"), size=.8)

We’re getting close but this is not what we want. We wanted grey and red. To change the color, we use scale_color_manual(). Scale_color_manual allows us to create our own discrete scale. The name argument will become our legend title and the values accepts a set of aesthetic values to map our data values to. If this is a named vector (i.e. ‘Important line’) then the values will be matched based on the names. If unnamed, values will be matched in order.

# we pass an empty value to name so the legend title will be blank
ggplot(nmmaps, aes(x=date, y=o3)) + geom_line(aes(color="Important line")) + geom_point(aes(color="Point values"), size=.8) + scale_color_manual(name='', values = c('Important line'='grey', 'Point values' = 'red'))

Tantalizingly close! But we don’t want a line with a point for both Line=grey and point=red. The final step is to override the aesthetics in the legend. The guide() function allows us to control guides like the legend.

When using guides(), the guides for each scale can be set in our call of scale_color_manual with the argument guide. Here we set guide=‘legend’, and then in our call to guides, we specify that we’d like to override the aesthetic of guide_legend

# Linetype:1 for Important line; Linetype:0 and shape:16 for Point values
ggplot(nmmaps, aes(x=date, y=o3)) + geom_line(aes(color="Important line")) + geom_point(aes(color="Point values"), size=.8) + scale_color_manual(name="", values= c('Important line' = 'grey', 'Point values' = 'red'), guide='legend') + guides(color=guide_legend(override.aes = list(linetype=c(1,0), shape=c(NA, 16))))

Working with background colors

Change the panel color: panel.background

ggplot(nmmaps, aes(x=date, y=temp)) + geom_point(color="tan3", size=.8) + theme(panel.background=element_rect(fill = "grey75")) 

Change the grid lines: panel.grid.major, panel.grid.minor

ggplot(nmmaps, aes(x=date, y=temp)) + geom_point(color="tan3", size=.8) + theme(panel.background = element_rect(fill = "grey75"), 
    panel.grid.major = element_line(color = "lightskyblue", size=1.2),
    panel.grid.minor = element_line(color="lightsteelblue2"))

Change the plot background (not the panel) color: plot.background

ggplot(nmmaps, aes(date, temp))+geom_point(color="tan3", size=.8)+
  theme(plot.background = element_rect(fill = 'gold2'),
        panel.background = element_rect(fill='yellowgreen')) 

Working with margins: plot.margin

We sometimes find that we need to add a little space to one margin of our plot. Similar to the previous examples we can use an argument to the theme() function. In this case the argument is plot.margin. This argument can handle a variety of different units (cm, inches etc) but it requires the use of the function unit from the package grid to specify the units. Here we will use a 6 cm margin on the right and left.

# Add extra space to both left and right
library(grid)
ggplot(nmmaps, aes(date, temp))+geom_point(color="tan3", size=.8)+
  theme(plot.background = element_rect(fill = 'gold2'),
        panel.background = element_rect(fill='yellowgreen'),
        plot.margin=unit(c(.5,3,.5,3), "cm")) #Top, right, bottom, left

Creating multi-panel plots

The ggplot2 package has two nice functions for creating multi-panel plots. They are related but a little different. facet_wrap creates essentially a ribbon of plots based on a single variable while facet_grid can take two variables.

Create a single row of plots based on one variable: facet_wrap()

# A row of plots based on one variable: year
ggplot(nmmaps, aes(date,temp))+geom_point(color="aquamarine4", size=0.8)+facet_wrap(~year, nrow=1)

Create a matrix of plots based on one variable: facet_wrap(nrow=2)

ggplot(nmmaps, aes(date, temp)) + geom_point(color="aquamarine4", size=0.5) + facet_wrap(~year, nrow=2)

Allow scales to roam free: scales

The default for multi-panel plots in ggplot2 is to use equivalent scales in each panel. But sometimes you want to allow a panel’s own data to determine the scale. This is not often a good idea since it may give your user the wrong impression about the data but to do this you can set scales=“free” like this:

ggplot(nmmaps, aes(date, temp)) + geom_point(color="aquamarine4", size=0.5) + facet_wrap(~year, ncol=2, scales="free")

Create a grid of plots using two variables: facet_grid()

ggplot(nmmaps, aes(date, temp)) + geom_point(color="firebrick", size=0.5) + facet_grid(year~season)

Put two potentially unrelated plots side by side: pushViewport(), grid.arrange()

myplot1<-ggplot(nmmaps, aes(date, temp))+geom_point(color="firebrick")
myplot2<-ggplot(nmmaps, aes(temp, o3))+geom_point(color="olivedrab")
library(grid)
pushViewport(viewport(layout = grid.layout(1,2)))
print(myplot1, vp = viewport(layout.pos.row = 1, layout.pos.col = 1))
print(myplot2, vp = viewport(layout.pos.row = 1, layout.pos.col = 2))
# alternatively, a little easier
library(gridExtra)
grid.arrange(myplot1, myplot2, ncol=2)

Working with themes

Use a new theme: load ggthemes, theme_xx()

library(ggthemes)
ggplot(nmmaps, aes(date, temp, color=factor(season))) + geom_point() + ggtitle("This plot looks different from the default") + theme_economist() + scale_colour_economist()

Change the size of all plot text elements: theme_set()

Personally, I find default size of the tick text, legends and other elements to be a little too small. Luckily it’s incredibly easy to change the size of all the text elements at once. If you look below at the section on creating a custom theme you’ll notice that the sizes of all the elements are relative (rel()) to the base_size. As a result, you can simply change the base_size and you’re done.

theme_set(theme_gray(base_size=20))
ggplot(nmmaps, aes(x=date, y=o3)) + geom_point(color="red")

Changing the default font size to something more sensible

theme_set(theme_gray(base_size=12))
ggplot(nmmaps, aes(x=date, y=o3)) + geom_point(color="blue")

Tips on creating a custom theme

If you want to change the theme for an entire session you can use theme_set as in theme_set(theme_bw()). The default is called theme_gray. If you wanted to create your own custom theme, you could extract the code directly from the gray theme and modify. Type the following into the console:

theme_gray
function (base_size = 11, base_family = "") 
{
    half_line <- base_size/2
    theme(line = element_line(colour = "black", size = 0.5, linetype = 1, 
        lineend = "butt"), rect = element_rect(fill = "white", 
        colour = "black", size = 0.5, linetype = 1), text = element_text(family = base_family, 
        face = "plain", colour = "black", size = base_size, lineheight = 0.9, 
        hjust = 0.5, vjust = 0.5, angle = 0, margin = margin(), 
        debug = FALSE), axis.line = element_blank(), axis.line.x = NULL, 
        axis.line.y = NULL, axis.text = element_text(size = rel(0.8), 
            colour = "grey30"), axis.text.x = element_text(margin = margin(t = 0.8 * 
            half_line/2), vjust = 1), axis.text.x.top = element_text(margin = margin(b = 0.8 * 
            half_line/2), vjust = 0), axis.text.y = element_text(margin = margin(r = 0.8 * 
            half_line/2), hjust = 1), axis.text.y.right = element_text(margin = margin(l = 0.8 * 
            half_line/2), hjust = 0), axis.ticks = element_line(colour = "grey20"), 
        axis.ticks.length = unit(half_line/2, "pt"), axis.title.x = element_text(margin = margin(t = half_line), 
            vjust = 1), axis.title.x.top = element_text(margin = margin(b = half_line), 
            vjust = 0), axis.title.y = element_text(angle = 90, 
            margin = margin(r = half_line), vjust = 1), axis.title.y.right = element_text(angle = -90, 
            margin = margin(l = half_line), vjust = 0), legend.background = element_rect(colour = NA), 
        legend.spacing = unit(0.4, "cm"), legend.spacing.x = NULL, 
        legend.spacing.y = NULL, legend.margin = margin(0.2, 
            0.2, 0.2, 0.2, "cm"), legend.key = element_rect(fill = "grey95", 
            colour = "white"), legend.key.size = unit(1.2, "lines"), 
        legend.key.height = NULL, legend.key.width = NULL, legend.text = element_text(size = rel(0.8)), 
        legend.text.align = NULL, legend.title = element_text(hjust = 0), 
        legend.title.align = NULL, legend.position = "right", 
        legend.direction = NULL, legend.justification = "center", 
        legend.box = NULL, legend.box.margin = margin(0, 0, 0, 
            0, "cm"), legend.box.background = element_blank(), 
        legend.box.spacing = unit(0.4, "cm"), panel.background = element_rect(fill = "grey92", 
            colour = NA), panel.border = element_blank(), panel.grid.major = element_line(colour = "white"), 
        panel.grid.minor = element_line(colour = "white", size = 0.25), 
        panel.spacing = unit(half_line, "pt"), panel.spacing.x = NULL, 
        panel.spacing.y = NULL, panel.ontop = FALSE, strip.background = element_rect(fill = "grey85", 
            colour = NA), strip.text = element_text(colour = "grey10", 
            size = rel(0.8)), strip.text.x = element_text(margin = margin(t = half_line, 
            b = half_line)), strip.text.y = element_text(angle = -90, 
            margin = margin(l = half_line, r = half_line)), strip.placement = "inside", 
        strip.placement.x = NULL, strip.placement.y = NULL, strip.switch.pad.grid = unit(0.1, 
            "cm"), strip.switch.pad.wrap = unit(0.1, "cm"), plot.background = element_rect(colour = "white"), 
        plot.title = element_text(size = rel(1.2), hjust = 0, 
            vjust = 1, margin = margin(b = half_line * 1.2)), 
        plot.subtitle = element_text(size = rel(0.9), hjust = 0, 
            vjust = 1, margin = margin(b = half_line * 0.9)), 
        plot.caption = element_text(size = rel(0.9), hjust = 1, 
            vjust = 1, margin = margin(t = half_line * 0.9)), 
        plot.margin = margin(half_line, half_line, half_line, 
            half_line), complete = TRUE)
}
<environment: namespace:ggplot2>

And we can copy + edit values according to the aesthetic needs of our theme.

Working with colors

For simple applications working with colors is straightforward in ggplot2 but when you have more advanced needs it can be a challenge. For a more advaned treatment of the topic you should probably get your hands on Hadley’s book which has nice coverage. There are a few other good sources including the R Cookbook and the ggplot2 online docs. Tian Zheng at Columbia has created a useful PDF of R colors.

In order to use color with your data, most importantly, you need to know if you’re dealing with a categorical or continuous variable.

Categorial variables - manually select the colors: scale_color_manual(name, values)

ggplot(nmmaps, aes(date, temp, color=factor(season))) + geom_point() + scale_color_manual(name="Season", values = c("dodgerblue4", "darkolivegreen4",
                              "darkorchid3", "goldenrod1"))

Categorial variables - using a built-in palette: scale_color_brewer()

ggplot(nmmaps, aes(date, temp, color=factor(season))) + geom_point() + scale_color_brewer(palette="Set1")

We can also use the Tableau colors if we have ggthemes: scale_color_tableau

library(ggthemes)
ggplot(nmmaps, aes(x=date, y=temp, color=factor(season)))+geom_point() + scale_color_tableau()

Color choice with continuous variables: scale_color_gradient(), scale_color_gradient2()

In our example we will change the color variable to ozone (o3), a continuous variable that is strongly related to temperature (higher temperature = higher ozone). The function scale_color_gradient() is a sequential gradient while scale_color_gradient2() is diverging.

Here is a default continuous color scheme (sequential color scheme):

ggplot(nmmaps, aes(x=date, y=temp, color=o3))+geom_point()

# this code produces an identical plot
ggplot(nmmaps, aes(date, temp, color=o3))+geom_point()+scale_color_gradient()

To manually change the low and high colors (sequential color scheme):

ggplot(nmmaps, aes(x=date, y=temp, color=o3)) + geom_point() + scale_color_gradient(low = "darkkhaki", high= "darkgreen")

The temperature data is normally distributed so how about a diverging color scheme (rather than sequential). For diverging color we can use the scale_color_gradient2 function with a midpoint parameter.

mid <- mean(nmmaps$o3)
ggplot(nmmaps, aes(date, temp, color=o3))+geom_point()+scale_color_gradient2(midpoint = mid, low="blue", mid="white", high="red")

Working with annotation

Add text annotation in the top-right, top-left etc: annotation_custom(), textGrob()

The grobTree function (from grid) creates a grid graphical object and textGrob creates the text graphical object. The annotation_custom() function comes from ggplot2 and is designed to use a grob as input.

library(grid)
my_grob <- grobTree(textGrob("This text stays in place!", x=0.1, y=0.95, hjust=0, gp = gpar(col="blue", fontsize=10, fontface="italic")))
ggplot(nmmaps, aes(temp, o3)) + geom_point(color="firebrick")+annotation_custom(my_grob)

‘Big deal’ you say!? It is a big deal. The value here is particularly evident when you have multiple plots with different scales. In the plot below you see that the axis scales vary yet the same code as above can be used to put the annotation is the same place on each facet. Nice!

library(grid)
my_grob <- grobTree(textGrob("This text stays in place!", x=0.1, y=0.95, hjust=0, gp = gpar(col="blue", fontsize=10, fontface="italic")))
ggplot(nmmaps, aes(temp, o3)) + geom_point(color="firebrick") + facet_wrap(~season, scales="free") + annotation_custom(my_grob)

Working with coordinates

Flip a plot on its side: coord_flip()

ggplot(nmmaps, aes(x=season, y=o3))+geom_boxplot(fill="chartreuse4")+coord_flip()

Working with plottypes

Alternatives to geom_point: geom_jitter()

Box plots are great, but they can be so incredibly boring. There are alternatives, first – a box plot:

g<-ggplot(nmmaps, aes(x=season, y=o3))
g+geom_boxplot(fill="darkseagreen4")

Effective, yes. Interesting, no. What if we plot the points themselves?

g+geom_point()

Not only boring but uninformative, you could add transparency to deal with overplotting, but this is not good either. Let’s try something else.

Try adding a little jitter to the data. I like this for in-house visualization but be careful using jittering because youre purposely adding noise to your data and this can result in misinterpretation of your data.

g+geom_jitter(alpha=0.5, aes(color=season), position=position_jitter(width=.2))

This is better and I think since we’re working with season, a variable everyone will be familiar with, the extra noise will not likely lead to confusion.

Alternatives to geom_boxplot: geom_violin()

Violin plots, similar to box plots except you’re using a kernel density to show where you have the most data, are a useful visualization.

g+geom_violin(alpha=0.5, color="gray")

What if we rotated and added the jittered points:

g+geom_violin(alpha=0.5, color="gray")+geom_jitter(alpha=0.5, aes(color=season), position = position_jitter(width=0.1), size=0.6)+coord_flip()

This is nice and I like it. But be wary of using unusual plot types, they take more time for your users to understand. Sometimes the simplest and most conventional plot type is your best bet when sharing with others. Box plots may be boring but people know how to interpret them immediately.

Add a ribbon to your plot: geom_ribbon()

This is not the perfect dataset for this, but using ribbon can be useful. In this example we will create a 30-day running average using the filter() function so that our ribbon is not too noisy.

# Add a filter
nmmaps$o3run<-as.numeric(filter(nmmaps$o3, rep(1/30,30), sides=2))
There were 12 warnings (use warnings() to see them)
ggplot(nmmaps, aes(date, o3run))+geom_line(color="lightpink4", lwd=1)

Note: what filter() does with filter size = 30 is explained in more details on this StackOverflow thread. Simply put, we create a running average and use both sides of the value (sides=2) instead of only using past values (sides=1). So on the 17th April, the o3run value would be the running average of 1st April to 30th April. If sides=1, it would instead have been the running average of 1st April to 17th April. Sides can only be either 1 or 2. Contrast this to the o3 value of 17th April which is just the value of 17th April alone.

How does it look if we fill in the area below the curve using the geom_ribbon() function?

ggplot(nmmaps, aes(date, o3run)) + geom_ribbon(aes(ymin=0, ymax=o3run), fill="lightpink3", color="lightpink3") + geom_line(color="lightpink4", lwd=1)
There were 24 warnings (use warnings() to see them)

For each x value, geom_ribbon displays a y interval defined by ymin and ymax. geom_area is a special case of geom_ribbon, where the ymin is fixed to 0.

Above is not really the conventional way to use geom_ribbon(), we would have used geom_area instead. Instead, why don’t we draw a ribbon that gives us one standard deviation above and below our data:

nmmaps$mino3 <- nmmaps$o3run-sd(nmmaps$o3run, na.rm=T)
There were 12 warnings (use warnings() to see them)
nmmaps$maxo3 <- nmmaps$o3run+sd(nmmaps$o3run, na.rm=T)
ggplot(nmmaps, aes(x=date, y=o3run))+geom_ribbon(aes(ymin = mino3, ymax = maxo3), fill="steelblue2", color="steelblue2") + geom_line(color="steelblue4", lwd=1)

Create a tiled correlation plot: geom_tile()

First step in creating a tiled correlation plot is to create the correlation matrix. We use Pearson because all the variables are fairly normally distributed in our nmmaps dataset. We may want to consider Spearman if our variables follow a different pattern. Note that since a correlation matrix has redundant information we will be setting half of it to NA.

# careful! We're sorting the field names so that the ordering in the final plot is correct. We call cor() to create the correlation matrix
thecor <- round(cor(nmmaps[,sort(c("death", "temp", "dewpoint", "pm10", "o3"))],
                    method = "pearson", use = "pairwise.complete.obs"),2)
thecor[lower.tri(thecor)] <- NA
thecor
         death dewpoint    o3 pm10  temp
death        1    -0.47 -0.24 0.00 -0.49
dewpoint    NA     1.00  0.45 0.33  0.96
o3          NA       NA  1.00 0.21  0.53
pm10        NA       NA    NA 1.00  0.37
temp        NA       NA    NA   NA  1.00

Now we will put it in a “long” format using the melt function from the reshape2 package and drop the records with NA values. Putting it in a long format makes it possible to map the aesthetic: aes(Var1, Var2)

library(reshape2)
Warning messages:
1: In get(results[[i]], pos = which(search() == packages[[i]])) :
  restarting interrupted promise evaluation
2: In get(results[[i]], pos = which(search() == packages[[i]])) :
  restarting interrupted promise evaluation
thecor <- melt(thecor)
thecor$Var1<-as.character(thecor$Var1)
thecor$Var2<-as.character(thecor$Var2)
thecor<-na.omit(thecor)
head(thecor)

We only printed the head but thecor actually has 25 rows (5 variables ^2)

Now for the plot. We are using geom_tile but if you have a lot of data you might consider geom_raster which can be much faster.

ggplot(thecor, aes(Var2, Var1)) + geom_tile(data=thecor, aes(fill=value), color="white") + scale_fill_gradient2(low="blue", high="red", mid="white", midpoint=0, limit=c(-1,1), name="Correlation\n(Pearson)") + theme(axis.text.x = element_text(angle=45, vjust=1, size=8, hjust=1), legend.title = element_text(color="darkred", size=8, face="bold")) + coord_equal()

Working with smooths

You’ve likely already learned how amazingly easy it is to add a smooth to your data using ggplot2. You can simply use stat_smooth() which will add a LOESS smooth if you have fewer than 1000 points or a GAM otherwise. Since we have more than 1000 points the smooth is a GAM.

Default - adding LOESS or GAM: stat_smooth()

ggplot(nmmaps, aes(date, temp))+geom_point(color="firebrick")+stat_smooth()
There were 24 warnings (use warnings() to see them)

Specifying the formula: stat_smooth(formula=)

But ggplot2 allows us to specify the model you want it to use. For example, let’s say we want to increase the GAM dimension (add some additional wiggles to the smooth):

ggplot(nmmaps, aes(date, temp)) + geom_point(color="grey") +   stat_smooth(method="gam", formula = y~s(x, k=10), col="darkolivegreen2", se=FALSE, size=1) + stat_smooth(method="gam", formula = y~s(x, k=30), col="red", se=FALSE, size=1) + stat_smooth(method="gam", formula = y~s(x, k=500), col="dodgerblue4", se=FALSE, size=1)

Adding a linear fit: stat_smooth(method=“lm”)

Although the default is smooth, it is also easy to add a standard linear fit

ggplot(nmmaps, aes(temp, death)) + geom_point(color="firebrick") + stat_smooth(method="lm", se=FALSE)
There were 12 warnings (use warnings() to see them)

Note that the same could be achieved using the more cumbersome:

lmTemp <- lm(death~temp, data=nmmaps)
There were 12 warnings (use warnings() to see them)
ggplot(nmmaps, aes(temp, death))+geom_point(col="firebrick")+geom_abline(intercept = lmTemp$coef[1], slope = lmTemp$coef[2])

The project is accomplished using R version 3.2.2 and ggplot2 version 2.2.1.

packageVersion("ggplot2")
[1] ‘2.2.1’
packageVersion("ggthemes")
[1] ‘3.3.0’
getRversion()
[1] ‘3.2.2’
LS0tCnRpdGxlOiAnQmVhdXRpZnVsIHBsb3R0aW5nIGluIFI6IEEgZ2dwbG90MiBjaGVhdHNoZWV0JwphdXRob3I6ICJTYW11ZWwgQ2hhbiIKZGF0ZTogIjE0LzAyLzIwMTciCm91dHB1dDoKICBodG1sX25vdGVib29rOiBkZWZhdWx0CiAgaHRtbF9kb2N1bWVudDogZGVmYXVsdAogIHBkZl9kb2N1bWVudDogZGVmYXVsdAotLS0KClRoaXMgaXMgYSByZXByb2R1Y3Rpb24gb2YgW0JlYXV0aWZ1bCBwbG90dGluZyBpbiBSOiBBIGdncGxvdDIgY2hlYXRzaGVldF0oaHR0cDovL3pldnJvc3MuY29tL2Jsb2cvMjAxNC8wOC8wNC9iZWF1dGlmdWwtcGxvdHRpbmctaW4tci1hLWdncGxvdDItY2hlYXRzaGVldC0zKSBieSBaZXYgUm9zcy4gVGhlIG1vdGl2YXRpb24gYmVoaW5kIHRoaXMgZXhlcmNpc2UgaXMgdG8gdGFrZSBhZHZhbnRhZ2Ugb2YgdGhlIFIgTm90ZWJvb2sgZm9ybWF0LCBhbGxvd2luZyB1cyB0byB2aXN1YWxpemUgdGhlIHBsb3R0aW5nIHJlc3VsdHMgYWZ0ZXIgZWFjaCBjb2RlIGNodW5rLiBDcmVkaXRzIHRvIHRoZSBvcmlnaW5hbCBhdXRob3IgWmV2IFJvc3MsIGZpcnN0IHB1Ymxpc2hlZCBvbiBBdWd1c3QgNCwgMjAxNC4KCiMgUXVpY2sgU2V0dXA6IFRoZSBkYXRhc2V0Cldl4oCZcmUgdXNpbmcgZGF0YSBmcm9tIHRoZSBOYXRpb25hbCBNb3JiaWRpdHkgYW5kIE1vcnRhbGl0eSBBaXIgUG9sbHV0aW9uIFN0dWR5IChOTU1BUFMpLiBUbyBtYWtlIHRoZSBwbG90cyBtYW5hZ2VhYmxlIHdl4oCZcmUgbGltaXRpbmcgdGhlIGRhdGEgdG8gQ2hpY2FnbyBhbmQgMTk5Ny0yMDAwLiBGb3IgbW9yZSBkZXRhaWwgb24gdGhpcyBkYXRhc2V0LCBjb25zdWx0IFJvZ2VyIFBlbmfigJlzIGJvb2sgW1N0YXRpc3RpY2FsIE1ldGhvZHMgaW4gRW52aXJvbm1lbnRhbCBFcGlkZW1pb2xvZ3kgd2l0aCBSXShodHRwOi8vd3d3LnNwcmluZ2VyLmNvbS9zdGF0aXN0aWNzL2xpZmUrc2NpZW5jZXMsK21lZGljaW5lKyUyNitoZWFsdGgvYm9vay85NzgtMC0zODctNzgxNjYtMikuCgpgYGB7cn0KIyBMb2FkIHRoZSByZXF1aXJlZCBsaWJyYXJ5IApsaWJyYXJ5KGdncGxvdDIpCgojIExvYWQgb3VyIGRhdGFzZXQKbm1tYXBzIDwtIHJlYWQuY3N2KCJjaGljYWdvLW5tbWFwcy5jc3YiLCBhcy5pcyA9IFQpCgojIFRha2UgZGF0YSBvbmx5IGFmdGVyIDEvMS8xOTk3Cm5tbWFwcyRkYXRlIDwtIGFzLkRhdGUobm1tYXBzJGRhdGUpCm5tbWFwcyA8LSBubW1hcHNbbm1tYXBzJGRhdGUgPiBhcy5EYXRlKCIxOTk2LTEyLTMxIiksXQoKIyBTdWJzdHJpbmcgdGhlIGZpcnN0IGZvdXIgZWxlbWVudHMgWzE6NF0gb2YgdGhlIGRhdGUgY2hhciBzbyAoIjE5OTctMDEtMDEiKSByZXR1cm5zICIxOTk3IgpubW1hcHMkeWVhciA8LSBzdWJzdHJpbmcobm1tYXBzJGRhdGUsIDEsNCkKaGVhZChubW1hcHMpCmBgYAoKIyBBIGRlZmF1bHQgcGxvdCBpbiBnZ3Bsb3QyCmBgYHtyfQpnIDwtIGdncGxvdChubW1hcHMsIGFlcyhkYXRlLCB0ZW1wKSkrZ2VvbV9wb2ludChjb2xvcj0iZmlyZWJyaWNrIikKZwpgYGAKCiMgV29ya2luZyB3aXRoIHRoZSB0aXRsZQojIyMgQWRkIGEgdGl0bGU6IGdndGl0bGUoKSBvciBsYWJzKCkKCmBgYHtyfQpnIDwtIGcgKyBnZ3RpdGxlKCdUZW1wZXJhdHVyZScpCmcKYGBgCkFsdGVybmF0aXZlbHksIHdlIGNhbiB1c2UgbGFicygpOgpnICsgbGFicyh0aXRsZSA9ICdUaXRsZScpCgojIyMgTWFrZSB0aXRsZSBib2xkIGFuZCBhZGQgYSBsaXR0bGUgc3BhY2UgYXQgdGhlIGJhc2VsaW5lOiBmYWNlLCBtYXJnaW4KTm90ZSB0aGF0IHRoZSAqKm1hcmdpbioqIGFyZ3VtZW50IHVzZXMgdGhlIG1hcmdpbiBmdW5jdGlvbiBhbmQgd2UgcHJvdmlkZSB0aGUgdG9wLCByaWdodCwgYm90dG9tLCBsZWZ0IG1hcmdpbnMgaW4gdGhhdCBvcmRlci4gVGhlIGRlZmF1bHQgdW5pdCBpcyBwb2ludHMuCmBgYHtyfQpnIDwtIGcgKyB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCwgZmFjZSA9ICJib2xkIiwgbWFyZ2luID0gbWFyZ2luKDEwLDAsMTAsMCkpKQpnCmBgYAoKIyMjIFVzaW5nIGEgbm9uLXRyYWRpdGlvbmFsIGZvbnQgaW4geW91ciB0aXRsZTogZmFtaWx5CmBgYHtyfQpsaWJyYXJ5KGV4dHJhZm9udCkKZyA8LSBnICsgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMjAsIGxpbmVoZWlnaHQgPSAuOCwgdmp1c3Q9MSwgZmFtaWx5ID0gIlJvYm90byBDb25kZW5zZWQiKSkKZwpgYGAKCiMjIyBDaGFuZ2Ugc3BhY2luZyBpbiBtdWx0aS1saW5lIHRleHQ6IGxpbmVoZWlnaHQKYGBge3J9CmcgPC0gZyArIGdndGl0bGUoIlRlbXBlcmF0dXJlIGZsdWN0dWF0aW9ucyBcbiBiZXR3ZWVuIDE5OTcgYW5kIDIwMDEiKQpnIDwtIGcgKyB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MjAsIGZhY2U9ImJvbGQiLCB2anVzdD0xLCBsaW5laGVpZ2h0ID0gLjgpKQpnCmBgYAoKIyBXb3JraW5nIHdpdGggYXhlcwojIyMgQWRkIHggYW5kIHkgYXhpcyBsYWJlbDogbGFicygpLCB4bGFiKCkKYGBge3J9CmcgPC0gZytsYWJzKHg9IkRhdGUiLCB5PWV4cHJlc3Npb24ocGFzdGUoIlRlbXBlcmF0dXJlICgiLCBkZWdyZWUgfiBGLCAiICkiKSksIHRpdGxlID0gIlRlbXBlcmF0dXJlIikKZwpgYGAKCiMjIyBHZXQgcmlkIG9mIGF4aXMgdGlja3MgYW5kIHRpY2sgdGV4dDogdGhlbWUoKSwgYXhpcy50aWNrcy55CmBgYHtyfQpnICsgdGhlbWUoYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgojIyMgQ2hhbmdlIHNpemUgb2YgYW5kIHJvdGF0ZSB0aWNrIHRleHQ6IGF4aXMudGV4dC54CmBgYHtyfQpnICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NTAsIHNpemU9MTIsIHZqdXN0PTAuNSkpCmBgYAoKIyMjIE1vdmUgdGhlIGxhYmVscyBhd2F5IGZyb20gdGhlIHBsb3QgYW5kIGFkZCBjb2xvcjogYXhpcy50aXRsZS54LCB2anVzdApgYGB7cn0KZyArIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChjb2xvcj0iZm9yZXN0Z3JlZW4iLCB2anVzdD0wLjM1KSwKICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChjb2xvcj0iY2FkZXRibHVlIiwgdmp1c3Q9MC4zNSkpCmBgYAoKIyMjIExpbWl0IGFuIGF4aXMgdG8gYSByYW5nZTogeWxpbSgpLCBzY2FsZV94X2NvbnRpbnVvdXMoKSwgY29vcmRfY2FydGVzaWFuKCkKYGBge3J9CmcgKyB5bGltKGMoMCw2MCkpCmBgYApBbHRlcm5hdGl2ZWx5OiBnICsgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cz1jKDAsMzUpKSArIGcrY29vcmRfY2FydGVzaWFuKHhsaW09YygwLDM1KSkuIApUaGUgZm9ybWVyIHJlbW92ZXMgYWxsIGRhdGEgcG9pbnRzIG91dHNpZGUgdGhlIHJhbmdlIGFuZCBzZWNvbmQgb25lIGFkanVzdHMgdGhlIHZpc2libGUgYXJlYS4KCiMjIyBJZiB5b3Ugd2FudCB0aGUgYXhlcyB0byBiZSB0aGUgc2FtZTogY29vcmRfZXF1YWwoKQpGb3IgZGVtbyBwdXJwb3Nlcywgd2Ugd2lsbCBwbG90IHRoZSB0ZW1wZXJhdHVyZSBhZ2FpbnN0IHRoZSB0ZW1wZXJhdHVyZSB3aXRoIHNvbWUgcmFuZG9tIG5vaXNlLiBXZSB3YW50IGJvdGggYXhlcyB0byBiZSB0aGUgc2FtZSBzY2FsZSAvIHNhbWUgcmFuZ2UuCmBgYHtyfQojIHJub3JtKDE0NjEsIG1lYW49MCwgc2Q9MjApIGNyZWF0ZXMgMTQ2MSBudW1iZXJzIGJldHdlZW4gMCB0byAyMApnZ3Bsb3Qobm1tYXBzLCBhZXModGVtcCwgdGVtcCArIHJub3JtKG5yb3cobm1tYXBzKSwgc2Q9MjApKSkgKyBnZW9tX3BvaW50KGNvbG9yPSJtZWRpdW1wdXJwbGUiKSAreGxpbShjKDAsMTUwKSkreWxpbShjKDAsMTUwKSkrY29vcmRfZXF1YWwoKQpgYGAKCiMjIyBVc2UgYSBmdW5jdGlvbiB0byBhbHRlciBsYWJlbHM6IGxhYmVsPWZ1bmN0aW9uKHgpe30KYGBge3J9CmdncGxvdChubW1hcHMsIGFlcyhkYXRlLCB0ZW1wKSkgKyBnZW9tX3BvaW50KGNvbG9yPSJncmV5IikgKyBsYWJzKHg9Ik1vbnRoIiwgeT0iVGVtcCIpICsgc2NhbGVfeV9jb250aW51b3VzKGxhYmVsPWZ1bmN0aW9uKHgpe3JldHVybiAocGFzdGUoIk15IHZhbHVlIGlzIiwgeCwgImRlZ3JlZXMiKSl9KQpgYGAKCiMgV29ya2luZyB3aXRoIHRoZSBsZWdlbmQKV2Ugd2lsbCBjb2xvciBjb2RlIHRoZSBwbG90IGJhc2VkIG9uIHNlYXNvbi4gV2Ugb2JzZXJ2ZSB0aGF0IGJ5IGRlZmF1bHQgdGhlIGxlZ2VuZCB0aXRsZSBpcyB3aGF0IHdlJ3ZlIHNwZWNpZmllZCBpbiB0aGUgY29sb3IgYXJndW1lbnQuCmBgYHtyfQpnIDwtIGdncGxvdChubW1hcHMsIGFlcyhkYXRlLCB0ZW1wLCBjb2xvcj1mYWN0b3Ioc2Vhc29uKSkpICsgZ2VvbV9wb2ludCgpCmcKYGBgCgojIyMgVHVybmluZyBvZmYgdGhlIGxlZ2VuZCB0aXRsZQpgYGB7cn0KZyArIHRoZW1lKGxlZ2VuZC50aXRsZT1lbGVtZW50X2JsYW5rKCkpCmBgYAoKIyBDaGFuZ2UgdGhlIHRpdGxlIG9mIHRoZSBsZWdlbmQ6IHNjYWxlX2NvbG9yX2Rpc2NyZXRlKG5hbWU9IlRpdGxlIikKVG8gY2hhbmdlIHRoZSB0aXRsZSBvZiB0aGUgbGVnZW5kIHdlIHdvdWxkIHVzZSB0aGUgbmFtZSBhcmd1bWVudCBpbiBvdXIgc2NhbGUgZnVuY3Rpb24uIFdpdGhvdXQgdGhlIHNjYWxlIGZ1bmN0aW9uLCB3ZSB3aWxsIG5lZWQgdG8gY2hhbmdlIHRoZSBkYXRhIGl0c2VsZiBzbyBpdCBoYXMgdGhlIHJpZ2h0IGZvcm1hdC4KYGBge3J9CmcgPC0gZ2dwbG90KG5tbWFwcywgYWVzKGRhdGUsIHRlbXAsIGNvbG9yPWZhY3RvcihzZWFzb24pKSkgKyBnZW9tX3BvaW50KCkKZyArIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChjb2xvcj0iY2hvY29sYXRlIiwgc2l6ZT0xMCwgZmFjZT0iYm9sZCIpKSArIHNjYWxlX2NvbG9yX2Rpc2NyZXRlKG5hbWU9IkNob2NvbGF0ZSBsZWdlbmQgdGV4dCIpCmBgYAoKIyMjIEFkZCBhIGJveCBhcm91bmQgbGVnZW5kOiBsZWdlbmQuYmFja2dyb3VuZApgYGB7cn0KZyArIHRoZW1lKGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGw9ImdyYXk5MCIsIHNpemU9LjgpKQpgYGAKCiMjIyBDaGFuZ2UgdGhlIGJveCBjb2xvciBpbiBlYWNoIGxlZ2VuZCBrZXk6IGxlZ2VuZC5rZXkoZWxlbWVudF9yZWN0KGZpbGw9InJlZCIpKQpgYGB7cn0KZyArIHRoZW1lKGxlZ2VuZC5rZXkgPSBlbGVtZW50X3JlY3QoZmlsbD0ibGlnaHRnb2xkZW5yb2QyIikpCmBgYAoKIyMjIENoYW5nZSB0aGUgcG9zaXRpb24gb2YgdGhlIGxlZ2VuZDogbGVnZW5kLnBvc2l0aW9uKCkKYGBge3J9CmcgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikKYGBgCgojIyMgQ2hhbmdlIHRoZSBzaXplIG9mIHRoZSBzeW1ib2xzIGluIHRoZSBsZWdlbmQga2V5cwpgYGB7cn0KZyArIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemU9NSkpKQpgYGAKCiMjIyBMZWF2ZSBhIGxheWVyIG9mZiB0aGUgbGVnZW5kOiBzaG93X2d1aWRlCgpMZXQncyBzYXkgd2UgaGF2ZSBhIHBvaW50IGxheWVyIGFuZCBhZGQgbGFiZWwgdGV4dCB0byBpdCB1c2luZyBnZW9tX3RleHQgKG5vdyB3ZSBoYXZlIHR3byBsYXllcnMpLCBieSBkZWZhdWx0IGJvdGggdGhlIHBvaW50cyBhbmQgdGhlIGxhYmVsIHRleHQgbGF5ZXJzIChhZ2FpbiwgdHdvIGxheWVycykgd2lsbCBlbmQgdXAgaW4gb3VyIGxlZ2VuZCBsaWtlIHRoaXM6IApgYGB7cn0KZyA8LSBnZ3Bsb3Qobm1tYXBzLCBhZXMoZGF0ZSwgdGVtcCwgY29sb3I9ZmFjdG9yKHNlYXNvbikpKSArIGdlb21fcG9pbnQoKQpnICsgZ2VvbV90ZXh0KGRhdGE9bm1tYXBzLCBhZXMoZGF0ZSwgdGVtcCwgbGFiZWw9cm91bmQodGVtcCkpLCBzaXplPTMpCmBgYAoKRm9ydHVuYXRlbHksIHdlIGNhbiB0dXJuIG9mZiBhIGxheWVyIGluIHRoZSBsZWdlbmQgdXNpbmcgc2hvd19ndWlkZT1GCmBgYHtyfQpnICsgZ2VvbV90ZXh0KGRhdGE9bm1tYXBzLCBhZXMoZGF0ZSwgdGVtcCwgbGFiZWw9cm91bmQodGVtcCkpLCBzaXplPTMsIHNob3dfZ3VpZGU9RikKYGBgCgojIyMgTWFudWFsbHkgYWRkaW5nIGxlZ2VuZCBpdGVtcwpnZ3Bsb3QyIHdpbGwgbm90IGFkZCBhIGxlZ2VuZCBhdXRvbWF0aWNhbGx5IHVubGVzcyB3ZSBtYXAgYWV0aGV0aWNzIChjb2xvciwgc2l6ZSBldGMpIHRvIGEgdmFyaWFibGUuIFRoZXJlIGFyZSB0aW1lcywgdGhvdWdoLCB0aGF0IHdlIHdhbnQgdG8gaGF2ZSBhIGxlZ2VuZCBzbyB0aGF0IGl04oCZcyBjbGVhciB3aGF0IHlvdeKAmXJlIHBsb3R0aW5nLiBIZXJlIGlzIHRoZSBkZWZhdWx0OgpgYGB7cn0KIyBObyBsZWdlbmQgYnkgZGVmYXVsdCBiZWNhdXNlIHdlIGRpZG4ndCBtYXAgYWVzIHRvIGEgdmFyaWFibGUKZ2dwbG90KG5tbWFwcywgYWVzKHg9ZGF0ZSwgeT1vMykpICsgZ2VvbV9saW5lKGNvbG9yPSJncmV5IikgKyBnZW9tX3BvaW50KGNvbG9yPSJyZWQiLCBzaXplPS44KQpgYGAKCldlIGNhbiBmb3JjZSBhIGxlZ2VuZCBieSBtYXBwaW5nIHRvIGEgInZhcmlhYmxlIi4gV2UgYXJlIG1hcHBpbmcgdGhlIGxpbmVzIGFuZCB0aGUgcG9pbnRzIHVzaW5nIGFlcyBhbmQgd2UgYXJlIG1hcHBpbmcgbm90IHRvIGEgdmFyaWFibGUgaW4gb3VyIGRhdGFzZXQgYnV0IHRvIGEgc2luZ2xlIHN0cmluZyAoc28gdGhhdCB3ZSBnZXQganVzdCBvbmUgY29sb3IgZm9yIGVhY2gpLgoKYGBge3J9CmdncGxvdChubW1hcHMsIGFlcyh4PWRhdGUsIHk9bzMpKSArIGdlb21fbGluZShhZXMoY29sb3I9IkltcG9ydGFudCBsaW5lcyIpKSArIGdlb21fcG9pbnQoYWVzKGNvbG9yPSJNeSBwb2ludHMiKSwgc2l6ZT0uOCkKYGBgCgpXZSdyZSBnZXR0aW5nIGNsb3NlIGJ1dCB0aGlzIGlzIG5vdCB3aGF0IHdlIHdhbnQuIFdlIHdhbnRlZCBncmV5IGFuZCByZWQuIFRvIGNoYW5nZSB0aGUgY29sb3IsIHdlIHVzZSBzY2FsZV9jb2xvcl9tYW51YWwoKS4gU2NhbGVfY29sb3JfbWFudWFsIGFsbG93cyB1cyB0byBjcmVhdGUgb3VyIG93biBkaXNjcmV0ZSBzY2FsZS4gVGhlICoqbmFtZSoqIGFyZ3VtZW50IHdpbGwgYmVjb21lIG91ciBsZWdlbmQgdGl0bGUgYW5kIHRoZSAqKnZhbHVlcyoqIGFjY2VwdHMgYSBzZXQgb2YgYWVzdGhldGljIHZhbHVlcyB0byBtYXAgb3VyIGRhdGEgdmFsdWVzIHRvLiBJZiB0aGlzIGlzIGEgbmFtZWQgdmVjdG9yIChpLmUuICdJbXBvcnRhbnQgbGluZScpIHRoZW4gdGhlIHZhbHVlcyB3aWxsIGJlIG1hdGNoZWQgYmFzZWQgb24gdGhlIG5hbWVzLiBJZiB1bm5hbWVkLCB2YWx1ZXMgd2lsbCBiZSBtYXRjaGVkIGluIG9yZGVyLgpgYGB7cn0KIyB3ZSBwYXNzIGFuIGVtcHR5IHZhbHVlIHRvIG5hbWUgc28gdGhlIGxlZ2VuZCB0aXRsZSB3aWxsIGJlIGJsYW5rCmdncGxvdChubW1hcHMsIGFlcyh4PWRhdGUsIHk9bzMpKSArIGdlb21fbGluZShhZXMoY29sb3I9IkltcG9ydGFudCBsaW5lIikpICsgZ2VvbV9wb2ludChhZXMoY29sb3I9IlBvaW50IHZhbHVlcyIpLCBzaXplPS44KSArIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lPScnLCB2YWx1ZXMgPSBjKCdJbXBvcnRhbnQgbGluZSc9J2dyZXknLCAnUG9pbnQgdmFsdWVzJyA9ICdyZWQnKSkKYGBgCiAgClRhbnRhbGl6aW5nbHkgY2xvc2UhIEJ1dCB3ZSBkb27igJl0IHdhbnQgYSBsaW5lIHdpdGggYSBwb2ludCBmb3IgYm90aCBMaW5lPWdyZXkgYW5kIHBvaW50PXJlZC4gVGhlIGZpbmFsIHN0ZXAgaXMgdG8gb3ZlcnJpZGUgdGhlIGFlc3RoZXRpY3MgaW4gdGhlIGxlZ2VuZC4gVGhlIGd1aWRlKCkgZnVuY3Rpb24gYWxsb3dzIHVzIHRvIGNvbnRyb2wgZ3VpZGVzIGxpa2UgdGhlIGxlZ2VuZC4KCldoZW4gdXNpbmcgZ3VpZGVzKCksIHRoZSBndWlkZXMgZm9yIGVhY2ggc2NhbGUgY2FuIGJlIHNldCBpbiBvdXIgY2FsbCBvZiBzY2FsZV9jb2xvcl9tYW51YWwgd2l0aCB0aGUgYXJndW1lbnQgKipndWlkZSoqLiBIZXJlIHdlIHNldCBndWlkZT0nbGVnZW5kJywgYW5kIHRoZW4gaW4gb3VyIGNhbGwgdG8gZ3VpZGVzLCB3ZSBzcGVjaWZ5IHRoYXQgd2UnZCBsaWtlIHRvIG92ZXJyaWRlIHRoZSBhZXN0aGV0aWMgb2YgZ3VpZGVfbGVnZW5kCmBgYHtyfQojIExpbmV0eXBlOjEgZm9yIEltcG9ydGFudCBsaW5lOyBMaW5ldHlwZTowIGFuZCBzaGFwZToxNiBmb3IgUG9pbnQgdmFsdWVzCmdncGxvdChubW1hcHMsIGFlcyh4PWRhdGUsIHk9bzMpKSArIGdlb21fbGluZShhZXMoY29sb3I9IkltcG9ydGFudCBsaW5lIikpICsgZ2VvbV9wb2ludChhZXMoY29sb3I9IlBvaW50IHZhbHVlcyIpLCBzaXplPS44KSArIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lPSIiLCB2YWx1ZXM9IGMoJ0ltcG9ydGFudCBsaW5lJyA9ICdncmV5JywgJ1BvaW50IHZhbHVlcycgPSAncmVkJyksIGd1aWRlPSdsZWdlbmQnKSArIGd1aWRlcyhjb2xvcj1ndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChsaW5ldHlwZT1jKDEsMCksIHNoYXBlPWMoTkEsIDE2KSkpKQpgYGAKCiMgV29ya2luZyB3aXRoIGJhY2tncm91bmQgY29sb3JzCiMjIyBDaGFuZ2UgdGhlIHBhbmVsIGNvbG9yOiBwYW5lbC5iYWNrZ3JvdW5kCmBgYHtyfQpnZ3Bsb3Qobm1tYXBzLCBhZXMoeD1kYXRlLCB5PXRlbXApKSArIGdlb21fcG9pbnQoY29sb3I9InRhbjMiLCBzaXplPS44KSArIHRoZW1lKHBhbmVsLmJhY2tncm91bmQ9ZWxlbWVudF9yZWN0KGZpbGwgPSAiZ3JleTc1IikpIApgYGAKCiMjIyBDaGFuZ2UgdGhlIGdyaWQgbGluZXM6IHBhbmVsLmdyaWQubWFqb3IsIHBhbmVsLmdyaWQubWlub3IKYGBge3J9CmdncGxvdChubW1hcHMsIGFlcyh4PWRhdGUsIHk9dGVtcCkpICsgZ2VvbV9wb2ludChjb2xvcj0idGFuMyIsIHNpemU9LjgpICsgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gImdyZXk3NSIpLCAKICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAibGlnaHRza3libHVlIiwgc2l6ZT0xLjIpLAogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfbGluZShjb2xvcj0ibGlnaHRzdGVlbGJsdWUyIikpCmBgYAoKIyMjIENoYW5nZSB0aGUgcGxvdCBiYWNrZ3JvdW5kIChub3QgdGhlIHBhbmVsKSBjb2xvcjogcGxvdC5iYWNrZ3JvdW5kCmBgYHtyfQpnZ3Bsb3Qobm1tYXBzLCBhZXMoZGF0ZSwgdGVtcCkpK2dlb21fcG9pbnQoY29sb3I9InRhbjMiLCBzaXplPS44KSsKICB0aGVtZShwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICdnb2xkMicpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbD0neWVsbG93Z3JlZW4nKSkgCmBgYAoKIyBXb3JraW5nIHdpdGggbWFyZ2luczogcGxvdC5tYXJnaW4KV2Ugc29tZXRpbWVzIGZpbmQgdGhhdCB3ZSBuZWVkIHRvIGFkZCBhIGxpdHRsZSBzcGFjZSB0byBvbmUgbWFyZ2luIG9mIG91ciBwbG90LiBTaW1pbGFyIHRvIHRoZSBwcmV2aW91cyBleGFtcGxlcyB3ZSBjYW4gdXNlIGFuIGFyZ3VtZW50IHRvIHRoZSB0aGVtZSgpIGZ1bmN0aW9uLiBJbiB0aGlzIGNhc2UgdGhlIGFyZ3VtZW50IGlzIHBsb3QubWFyZ2luLiBUaGlzIGFyZ3VtZW50IGNhbiBoYW5kbGUgYSB2YXJpZXR5IG9mIGRpZmZlcmVudCB1bml0cyAoY20sIGluY2hlcyBldGMpIGJ1dCBpdCByZXF1aXJlcyB0aGUgdXNlIG9mIHRoZSBmdW5jdGlvbiB1bml0IGZyb20gdGhlIHBhY2thZ2UgKipncmlkKiogdG8gc3BlY2lmeSB0aGUgdW5pdHMuIEhlcmUgd2Ugd2lsbCB1c2UgYSA2IGNtIG1hcmdpbiBvbiB0aGUgcmlnaHQgYW5kIGxlZnQuCmBgYHtyfQojIEFkZCBleHRyYSBzcGFjZSB0byBib3RoIGxlZnQgYW5kIHJpZ2h0CmxpYnJhcnkoZ3JpZCkKZ2dwbG90KG5tbWFwcywgYWVzKGRhdGUsIHRlbXApKStnZW9tX3BvaW50KGNvbG9yPSJ0YW4zIiwgc2l6ZT0uOCkrCiAgdGhlbWUocGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAnZ29sZDInKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGw9J3llbGxvd2dyZWVuJyksCiAgICAgICAgcGxvdC5tYXJnaW49dW5pdChjKC41LDMsLjUsMyksICJjbSIpKSAjVG9wLCByaWdodCwgYm90dG9tLCBsZWZ0CmBgYAoKIyBDcmVhdGluZyBtdWx0aS1wYW5lbCBwbG90cwpUaGUgZ2dwbG90MiBwYWNrYWdlIGhhcyB0d28gbmljZSBmdW5jdGlvbnMgZm9yIGNyZWF0aW5nIG11bHRpLXBhbmVsIHBsb3RzLiBUaGV5IGFyZSByZWxhdGVkIGJ1dCBhIGxpdHRsZSBkaWZmZXJlbnQuICoqZmFjZXRfd3JhcCoqIGNyZWF0ZXMgZXNzZW50aWFsbHkgYSByaWJib24gb2YgcGxvdHMgYmFzZWQgb24gYSBzaW5nbGUgdmFyaWFibGUgd2hpbGUgKipmYWNldF9ncmlkKiogY2FuIHRha2UgdHdvIHZhcmlhYmxlcy4KCiMjIyBDcmVhdGUgYSBzaW5nbGUgcm93IG9mIHBsb3RzIGJhc2VkIG9uIG9uZSB2YXJpYWJsZTogZmFjZXRfd3JhcCgpCmBgYHtyfQojIEEgcm93IG9mIHBsb3RzIGJhc2VkIG9uIG9uZSB2YXJpYWJsZTogeWVhcgpnZ3Bsb3Qobm1tYXBzLCBhZXMoZGF0ZSx0ZW1wKSkrZ2VvbV9wb2ludChjb2xvcj0iYXF1YW1hcmluZTQiLCBzaXplPTAuOCkrZmFjZXRfd3JhcCh+eWVhciwgbnJvdz0xKQpgYGAKCiMjIyBDcmVhdGUgYSBtYXRyaXggb2YgcGxvdHMgYmFzZWQgb24gb25lIHZhcmlhYmxlOiBmYWNldF93cmFwKG5yb3c9MikKYGBge3J9CmdncGxvdChubW1hcHMsIGFlcyhkYXRlLCB0ZW1wKSkgKyBnZW9tX3BvaW50KGNvbG9yPSJhcXVhbWFyaW5lNCIsIHNpemU9MC41KSArIGZhY2V0X3dyYXAofnllYXIsIG5yb3c9MikKYGBgCgojIyMgQWxsb3cgc2NhbGVzIHRvIHJvYW0gZnJlZTogc2NhbGVzClRoZSBkZWZhdWx0IGZvciBtdWx0aS1wYW5lbCBwbG90cyBpbiBnZ3Bsb3QyIGlzIHRvIHVzZSBlcXVpdmFsZW50IHNjYWxlcyBpbiBlYWNoIHBhbmVsLiBCdXQgc29tZXRpbWVzIHlvdSB3YW50IHRvIGFsbG93IGEgcGFuZWzigJlzIG93biBkYXRhIHRvIGRldGVybWluZSB0aGUgc2NhbGUuIFRoaXMgaXMgbm90IG9mdGVuIGEgZ29vZCBpZGVhIHNpbmNlIGl0IG1heSBnaXZlIHlvdXIgdXNlciB0aGUgd3JvbmcgaW1wcmVzc2lvbiBhYm91dCB0aGUgZGF0YSBidXQgdG8gZG8gdGhpcyB5b3UgY2FuIHNldCBzY2FsZXM9ImZyZWUiIGxpa2UgdGhpczoKYGBge3J9CmdncGxvdChubW1hcHMsIGFlcyhkYXRlLCB0ZW1wKSkgKyBnZW9tX3BvaW50KGNvbG9yPSJhcXVhbWFyaW5lNCIsIHNpemU9MC41KSArIGZhY2V0X3dyYXAofnllYXIsIG5jb2w9Miwgc2NhbGVzPSJmcmVlIikKYGBgCgojIyMgQ3JlYXRlIGEgZ3JpZCBvZiBwbG90cyB1c2luZyB0d28gdmFyaWFibGVzOiBmYWNldF9ncmlkKCkKYGBge3J9CmdncGxvdChubW1hcHMsIGFlcyhkYXRlLCB0ZW1wKSkgKyBnZW9tX3BvaW50KGNvbG9yPSJmaXJlYnJpY2siLCBzaXplPTAuNSkgKyBmYWNldF9ncmlkKHllYXJ+c2Vhc29uKQpgYGAKCiMjIyBQdXQgdHdvIHBvdGVudGlhbGx5IHVucmVsYXRlZCBwbG90cyBzaWRlIGJ5IHNpZGU6IHB1c2hWaWV3cG9ydCgpLCBncmlkLmFycmFuZ2UoKQpgYGB7cn0KbXlwbG90MTwtZ2dwbG90KG5tbWFwcywgYWVzKGRhdGUsIHRlbXApKStnZW9tX3BvaW50KGNvbG9yPSJmaXJlYnJpY2siKQpteXBsb3QyPC1nZ3Bsb3Qobm1tYXBzLCBhZXModGVtcCwgbzMpKStnZW9tX3BvaW50KGNvbG9yPSJvbGl2ZWRyYWIiKQoKbGlicmFyeShncmlkKQpwdXNoVmlld3BvcnQodmlld3BvcnQobGF5b3V0ID0gZ3JpZC5sYXlvdXQoMSwyKSkpCnByaW50KG15cGxvdDEsIHZwID0gdmlld3BvcnQobGF5b3V0LnBvcy5yb3cgPSAxLCBsYXlvdXQucG9zLmNvbCA9IDEpKQpwcmludChteXBsb3QyLCB2cCA9IHZpZXdwb3J0KGxheW91dC5wb3Mucm93ID0gMSwgbGF5b3V0LnBvcy5jb2wgPSAyKSkKYGBgCgpgYGB7ciwgd2FybmluZz1GQUxTRX0KIyBhbHRlcm5hdGl2ZWx5LCBhIGxpdHRsZSBlYXNpZXIKbGlicmFyeShncmlkRXh0cmEpCmdyaWQuYXJyYW5nZShteXBsb3QxLCBteXBsb3QyLCBuY29sPTIpCmBgYAoKIyBXb3JraW5nIHdpdGggdGhlbWVzCiMjIyBVc2UgYSBuZXcgdGhlbWU6IGxvYWQgZ2d0aGVtZXMsIHRoZW1lX3h4KCkKYGBge3IsIHdhcm5pbmcgPSBGQUxTRX0KbGlicmFyeShnZ3RoZW1lcykKZ2dwbG90KG5tbWFwcywgYWVzKGRhdGUsIHRlbXAsIGNvbG9yPWZhY3RvcihzZWFzb24pKSkgKyBnZW9tX3BvaW50KCkgKyBnZ3RpdGxlKCJUaGlzIHBsb3QgbG9va3MgZGlmZmVyZW50IGZyb20gdGhlIGRlZmF1bHQiKSArIHRoZW1lX2Vjb25vbWlzdCgpICsgc2NhbGVfY29sb3VyX2Vjb25vbWlzdCgpCgpgYGAKCiMjIyBDaGFuZ2UgdGhlIHNpemUgb2YgYWxsIHBsb3QgdGV4dCBlbGVtZW50czogdGhlbWVfc2V0KCkKUGVyc29uYWxseSwgSSBmaW5kIGRlZmF1bHQgc2l6ZSBvZiB0aGUgdGljayB0ZXh0LCBsZWdlbmRzIGFuZCBvdGhlciBlbGVtZW50cyB0byBiZSBhIGxpdHRsZSB0b28gc21hbGwuIEx1Y2tpbHkgaXTigJlzIGluY3JlZGlibHkgZWFzeSB0byBjaGFuZ2UgdGhlIHNpemUgb2YgYWxsIHRoZSB0ZXh0IGVsZW1lbnRzIGF0IG9uY2UuIElmIHlvdSBsb29rIGJlbG93IGF0IHRoZSBzZWN0aW9uIG9uIGNyZWF0aW5nIGEgY3VzdG9tIHRoZW1lIHlvdeKAmWxsIG5vdGljZSB0aGF0IHRoZSBzaXplcyBvZiBhbGwgdGhlIGVsZW1lbnRzIGFyZSByZWxhdGl2ZSAocmVsKCkpIHRvIHRoZSBiYXNlX3NpemUuIEFzIGEgcmVzdWx0LCB5b3UgY2FuIHNpbXBseSBjaGFuZ2UgdGhlIGJhc2Vfc2l6ZSBhbmQgeW914oCZcmUgZG9uZS4KCmBgYHtyfQp0aGVtZV9zZXQodGhlbWVfZ3JheShiYXNlX3NpemU9MjApKQpnZ3Bsb3Qobm1tYXBzLCBhZXMoeD1kYXRlLCB5PW8zKSkgKyBnZW9tX3BvaW50KGNvbG9yPSJyZWQiKQpgYGAKCkNoYW5naW5nIHRoZSBkZWZhdWx0IGZvbnQgc2l6ZSB0byBzb21ldGhpbmcgbW9yZSBzZW5zaWJsZQpgYGB7cn0KdGhlbWVfc2V0KHRoZW1lX2dyYXkoYmFzZV9zaXplPTEyKSkKZ2dwbG90KG5tbWFwcywgYWVzKHg9ZGF0ZSwgeT1vMykpICsgZ2VvbV9wb2ludChjb2xvcj0iYmx1ZSIpCmBgYAoKIyMjIFRpcHMgb24gY3JlYXRpbmcgYSBjdXN0b20gdGhlbWUKSWYgeW91IHdhbnQgdG8gY2hhbmdlIHRoZSB0aGVtZSBmb3IgYW4gZW50aXJlIHNlc3Npb24geW91IGNhbiB1c2UgdGhlbWVfc2V0IGFzIGluIHRoZW1lX3NldCh0aGVtZV9idygpKS4gVGhlIGRlZmF1bHQgaXMgY2FsbGVkIHRoZW1lX2dyYXkuIElmIHlvdSB3YW50ZWQgdG8gY3JlYXRlIHlvdXIgb3duIGN1c3RvbSB0aGVtZSwgeW91IGNvdWxkIGV4dHJhY3QgdGhlIGNvZGUgZGlyZWN0bHkgZnJvbSB0aGUgZ3JheSB0aGVtZSBhbmQgbW9kaWZ5LiBUeXBlIHRoZSBmb2xsb3dpbmcgaW50byB0aGUgY29uc29sZTogCmBgYHtyfQp0aGVtZV9ncmF5CmBgYApBbmQgd2UgY2FuIGNvcHkgKyBlZGl0IHZhbHVlcyBhY2NvcmRpbmcgdG8gdGhlIGFlc3RoZXRpYyBuZWVkcyBvZiBvdXIgdGhlbWUuCgojIFdvcmtpbmcgd2l0aCBjb2xvcnMKRm9yIHNpbXBsZSBhcHBsaWNhdGlvbnMgd29ya2luZyB3aXRoIGNvbG9ycyBpcyBzdHJhaWdodGZvcndhcmQgaW4gZ2dwbG90MiBidXQgd2hlbiB5b3UgaGF2ZSBtb3JlIGFkdmFuY2VkIG5lZWRzIGl0IGNhbiBiZSBhIGNoYWxsZW5nZS4gRm9yIGEgbW9yZSBhZHZhbmVkIHRyZWF0bWVudCBvZiB0aGUgdG9waWMgeW91IHNob3VsZCBwcm9iYWJseSBnZXQgeW91ciBoYW5kcyBvbiBbSGFkbGV54oCZcyBib29rXShodHRwOi8vd3d3LnNwcmluZ2VyLmNvbS9zdGF0aXN0aWNzL2NvbXB1dGF0aW9uYWwrc3RhdGlzdGljcy9ib29rLzk3OC0wLTM4Ny05ODE0MC02KSB3aGljaCBoYXMgbmljZSBjb3ZlcmFnZS4gVGhlcmUgYXJlIGEgZmV3IG90aGVyIGdvb2Qgc291cmNlcyBpbmNsdWRpbmcgdGhlIFtSIENvb2tib29rXShodHRwOi8vd3d3LmNvb2tib29rLXIuY29tL0dyYXBocy9Db2xvcnNfKGdncGxvdDIpKSBhbmQgdGhlIFtnZ3Bsb3QyIG9ubGluZSBkb2NzXShodHRwOi8vZG9jcy5nZ3Bsb3QyLm9yZy9jdXJyZW50LykuIFRpYW4gWmhlbmcgYXQgQ29sdW1iaWEgaGFzIGNyZWF0ZWQgYSB1c2VmdWwgW1BERiBvZiBSIGNvbG9yc10oaHR0cDovL3d3dy5zdGF0LmNvbHVtYmlhLmVkdS9+dHpoZW5nL2ZpbGVzL1Jjb2xvci5wZGYpLgoKSW4gb3JkZXIgdG8gdXNlIGNvbG9yIHdpdGggeW91ciBkYXRhLCBtb3N0IGltcG9ydGFudGx5LCB5b3UgbmVlZCB0byBrbm93IGlmIHlvdeKAmXJlIGRlYWxpbmcgd2l0aCBhIGNhdGVnb3JpY2FsIG9yIGNvbnRpbnVvdXMgdmFyaWFibGUuCgojIyMgQ2F0ZWdvcmlhbCB2YXJpYWJsZXMgLSBtYW51YWxseSBzZWxlY3QgdGhlIGNvbG9yczogc2NhbGVfY29sb3JfbWFudWFsKG5hbWUsIHZhbHVlcykKYGBge3J9CmdncGxvdChubW1hcHMsIGFlcyhkYXRlLCB0ZW1wLCBjb2xvcj1mYWN0b3Ioc2Vhc29uKSkpICsgZ2VvbV9wb2ludCgpICsgc2NhbGVfY29sb3JfbWFudWFsKG5hbWU9IlNlYXNvbiIsIHZhbHVlcyA9IGMoImRvZGdlcmJsdWU0IiwgImRhcmtvbGl2ZWdyZWVuNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJkYXJrb3JjaGlkMyIsICJnb2xkZW5yb2QxIikpCmBgYAoKIyMjIENhdGVnb3JpYWwgdmFyaWFibGVzIC0gdXNpbmcgYSBidWlsdC1pbiBwYWxldHRlOiBzY2FsZV9jb2xvcl9icmV3ZXIoKQpgYGB7cn0KZ2dwbG90KG5tbWFwcywgYWVzKGRhdGUsIHRlbXAsIGNvbG9yPWZhY3RvcihzZWFzb24pKSkgKyBnZW9tX3BvaW50KCkgKyBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZT0iU2V0MSIpCmBgYAoKV2UgY2FuIGFsc28gdXNlIHRoZSBUYWJsZWF1IGNvbG9ycyBpZiB3ZSBoYXZlIGdndGhlbWVzOiBzY2FsZV9jb2xvcl90YWJsZWF1CmBgYHtyLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KGdndGhlbWVzKQpnZ3Bsb3Qobm1tYXBzLCBhZXMoeD1kYXRlLCB5PXRlbXAsIGNvbG9yPWZhY3RvcihzZWFzb24pKSkrZ2VvbV9wb2ludCgpICsgc2NhbGVfY29sb3JfdGFibGVhdSgpCmBgYAoKIyMjIENvbG9yIGNob2ljZSB3aXRoIGNvbnRpbnVvdXMgdmFyaWFibGVzOiBzY2FsZV9jb2xvcl9ncmFkaWVudCgpLCBzY2FsZV9jb2xvcl9ncmFkaWVudDIoKQpJbiBvdXIgZXhhbXBsZSB3ZSB3aWxsIGNoYW5nZSB0aGUgY29sb3IgdmFyaWFibGUgdG8gb3pvbmUgKG8zKSwgYSBjb250aW51b3VzIHZhcmlhYmxlIHRoYXQgaXMgc3Ryb25nbHkgcmVsYXRlZCB0byB0ZW1wZXJhdHVyZSAoaGlnaGVyIHRlbXBlcmF0dXJlID0gaGlnaGVyIG96b25lKS4gVGhlIGZ1bmN0aW9uIHNjYWxlX2NvbG9yX2dyYWRpZW50KCkgaXMgYSBzZXF1ZW50aWFsIGdyYWRpZW50IHdoaWxlIHNjYWxlX2NvbG9yX2dyYWRpZW50MigpIGlzIGRpdmVyZ2luZy4KCkhlcmUgaXMgYSBkZWZhdWx0IGNvbnRpbnVvdXMgY29sb3Igc2NoZW1lIChzZXF1ZW50aWFsIGNvbG9yIHNjaGVtZSk6CmBgYHtyfQpnZ3Bsb3Qobm1tYXBzLCBhZXMoeD1kYXRlLCB5PXRlbXAsIGNvbG9yPW8zKSkrZ2VvbV9wb2ludCgpCmBgYAoKYGBge3J9CiMgdGhpcyBjb2RlIHByb2R1Y2VzIGFuIGlkZW50aWNhbCBwbG90CmdncGxvdChubW1hcHMsIGFlcyhkYXRlLCB0ZW1wLCBjb2xvcj1vMykpK2dlb21fcG9pbnQoKStzY2FsZV9jb2xvcl9ncmFkaWVudCgpCmBgYAoKVG8gbWFudWFsbHkgY2hhbmdlIHRoZSBsb3cgYW5kIGhpZ2ggY29sb3JzIChzZXF1ZW50aWFsIGNvbG9yIHNjaGVtZSk6CmBgYHtyfQpnZ3Bsb3Qobm1tYXBzLCBhZXMoeD1kYXRlLCB5PXRlbXAsIGNvbG9yPW8zKSkgKyBnZW9tX3BvaW50KCkgKyBzY2FsZV9jb2xvcl9ncmFkaWVudChsb3cgPSAiZGFya2toYWtpIiwgaGlnaD0gImRhcmtncmVlbiIpCmBgYAoKVGhlIHRlbXBlcmF0dXJlIGRhdGEgaXMgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgc28gaG93IGFib3V0IGEgZGl2ZXJnaW5nIGNvbG9yIHNjaGVtZSAocmF0aGVyIHRoYW4gc2VxdWVudGlhbCkuIEZvciBkaXZlcmdpbmcgY29sb3Igd2UgY2FuIHVzZSB0aGUgc2NhbGVfY29sb3JfZ3JhZGllbnQyIGZ1bmN0aW9uIHdpdGggYSBtaWRwb2ludCBwYXJhbWV0ZXIuCgpgYGB7cn0KbWlkIDwtIG1lYW4obm1tYXBzJG8zKQpnZ3Bsb3Qobm1tYXBzLCBhZXMoZGF0ZSwgdGVtcCwgY29sb3I9bzMpKStnZW9tX3BvaW50KCkrc2NhbGVfY29sb3JfZ3JhZGllbnQyKG1pZHBvaW50ID0gbWlkLCBsb3c9ImJsdWUiLCBtaWQ9IndoaXRlIiwgaGlnaD0icmVkIikKYGBgCgojIFdvcmtpbmcgd2l0aCBhbm5vdGF0aW9uCiMjIyBBZGQgdGV4dCBhbm5vdGF0aW9uIGluIHRoZSB0b3AtcmlnaHQsIHRvcC1sZWZ0IGV0YzogYW5ub3RhdGlvbl9jdXN0b20oKSwgdGV4dEdyb2IoKQpUaGUgZ3JvYlRyZWUgZnVuY3Rpb24gKGZyb20gZ3JpZCkgY3JlYXRlcyBhIGdyaWQgZ3JhcGhpY2FsIG9iamVjdCBhbmQgdGV4dEdyb2IgY3JlYXRlcyB0aGUgdGV4dCBncmFwaGljYWwgb2JqZWN0LiBUaGUgYW5ub3RhdGlvbl9jdXN0b20oKSBmdW5jdGlvbiBjb21lcyBmcm9tIGdncGxvdDIgYW5kIGlzIGRlc2lnbmVkIHRvIHVzZSBhIGdyb2IgYXMgaW5wdXQuCgpgYGB7cn0KbGlicmFyeShncmlkKQoKbXlfZ3JvYiA8LSBncm9iVHJlZSh0ZXh0R3JvYigiVGhpcyB0ZXh0IHN0YXlzIGluIHBsYWNlISIsIHg9MC4xLCB5PTAuOTUsIGhqdXN0PTAsIGdwID0gZ3Bhcihjb2w9ImJsdWUiLCBmb250c2l6ZT0xMCwgZm9udGZhY2U9Iml0YWxpYyIpKSkKCmdncGxvdChubW1hcHMsIGFlcyh0ZW1wLCBvMykpICsgZ2VvbV9wb2ludChjb2xvcj0iZmlyZWJyaWNrIikrYW5ub3RhdGlvbl9jdXN0b20obXlfZ3JvYikKYGBgCgonQmlnIGRlYWwnIHlvdSBzYXkhPyBJdCBpcyBhIGJpZyBkZWFsLiBUaGUgdmFsdWUgaGVyZSBpcyBwYXJ0aWN1bGFybHkgZXZpZGVudCB3aGVuIHlvdSBoYXZlIG11bHRpcGxlIHBsb3RzIHdpdGggZGlmZmVyZW50IHNjYWxlcy4gSW4gdGhlIHBsb3QgYmVsb3cgeW91IHNlZSB0aGF0IHRoZSBheGlzIHNjYWxlcyB2YXJ5IHlldCB0aGUgc2FtZSBjb2RlIGFzIGFib3ZlIGNhbiBiZSB1c2VkIHRvIHB1dCB0aGUgYW5ub3RhdGlvbiBpcyB0aGUgc2FtZSBwbGFjZSBvbiBlYWNoIGZhY2V0LiBOaWNlIQoKYGBge3J9CmxpYnJhcnkoZ3JpZCkKCm15X2dyb2IgPC0gZ3JvYlRyZWUodGV4dEdyb2IoIlRoaXMgdGV4dCBzdGF5cyBpbiBwbGFjZSEiLCB4PTAuMSwgeT0wLjk1LCBoanVzdD0wLCBncCA9IGdwYXIoY29sPSJibHVlIiwgZm9udHNpemU9MTAsIGZvbnRmYWNlPSJpdGFsaWMiKSkpCgpnZ3Bsb3Qobm1tYXBzLCBhZXModGVtcCwgbzMpKSArIGdlb21fcG9pbnQoY29sb3I9ImZpcmVicmljayIpICsgZmFjZXRfd3JhcCh+c2Vhc29uLCBzY2FsZXM9ImZyZWUiKSArIGFubm90YXRpb25fY3VzdG9tKG15X2dyb2IpCmBgYAoKIyBXb3JraW5nIHdpdGggY29vcmRpbmF0ZXMKIyMjIEZsaXAgYSBwbG90IG9uIGl0cyBzaWRlOiBjb29yZF9mbGlwKCkKYGBge3J9CmdncGxvdChubW1hcHMsIGFlcyh4PXNlYXNvbiwgeT1vMykpK2dlb21fYm94cGxvdChmaWxsPSJjaGFydHJldXNlNCIpK2Nvb3JkX2ZsaXAoKQpgYGAKCiMgV29ya2luZyB3aXRoIHBsb3R0eXBlcwojIyMgQWx0ZXJuYXRpdmVzIHRvIGdlb21fcG9pbnQ6IGdlb21faml0dGVyKCkKCkJveCBwbG90cyBhcmUgZ3JlYXQsIGJ1dCB0aGV5IGNhbiBiZSBzbyBpbmNyZWRpYmx5IGJvcmluZy4gVGhlcmUgYXJlIGFsdGVybmF0aXZlcywgZmlyc3Qg4oCTIGEgYm94IHBsb3Q6CmBgYHtyfQpnPC1nZ3Bsb3Qobm1tYXBzLCBhZXMoeD1zZWFzb24sIHk9bzMpKQpnK2dlb21fYm94cGxvdChmaWxsPSJkYXJrc2VhZ3JlZW40IikKYGBgCgpFZmZlY3RpdmUsIHllcy4gSW50ZXJlc3RpbmcsIG5vLiBXaGF0IGlmIHdlIHBsb3QgdGhlIHBvaW50cyB0aGVtc2VsdmVzPwpgYGB7cn0KZytnZW9tX3BvaW50KCkKYGBgCk5vdCBvbmx5IGJvcmluZyBidXQgdW5pbmZvcm1hdGl2ZSwgeW91IGNvdWxkIGFkZCB0cmFuc3BhcmVuY3kgdG8gZGVhbCB3aXRoIG92ZXJwbG90dGluZywgYnV0IHRoaXMgaXMgbm90IGdvb2QgZWl0aGVyLiBMZXTigJlzIHRyeSBzb21ldGhpbmcgZWxzZS4KClRyeSBhZGRpbmcgYSBsaXR0bGUgaml0dGVyIHRvIHRoZSBkYXRhLiBJIGxpa2UgdGhpcyBmb3IgaW4taG91c2UgdmlzdWFsaXphdGlvbiBidXQgYmUgY2FyZWZ1bCB1c2luZyBqaXR0ZXJpbmcgYmVjYXVzZSB5b3VyZSBwdXJwb3NlbHkgYWRkaW5nIG5vaXNlIHRvIHlvdXIgZGF0YSBhbmQgdGhpcyBjYW4gcmVzdWx0IGluIG1pc2ludGVycHJldGF0aW9uIG9mIHlvdXIgZGF0YS4KYGBge3J9CmcrZ2VvbV9qaXR0ZXIoYWxwaGE9MC41LCBhZXMoY29sb3I9c2Vhc29uKSwgcG9zaXRpb249cG9zaXRpb25faml0dGVyKHdpZHRoPS4yKSkKYGBgClRoaXMgaXMgYmV0dGVyIGFuZCBJIHRoaW5rIHNpbmNlIHdl4oCZcmUgd29ya2luZyB3aXRoIHNlYXNvbiwgYSB2YXJpYWJsZSBldmVyeW9uZSB3aWxsIGJlIGZhbWlsaWFyIHdpdGgsIHRoZSBleHRyYSBub2lzZSB3aWxsIG5vdCBsaWtlbHkgbGVhZCB0byBjb25mdXNpb24uCgojIyMgQWx0ZXJuYXRpdmVzIHRvIGdlb21fYm94cGxvdDogZ2VvbV92aW9saW4oKQpWaW9saW4gcGxvdHMsIHNpbWlsYXIgdG8gYm94IHBsb3RzIGV4Y2VwdCB5b3XigJlyZSB1c2luZyBhIGtlcm5lbCBkZW5zaXR5IHRvIHNob3cgd2hlcmUgeW91IGhhdmUgdGhlIG1vc3QgZGF0YSwgYXJlIGEgdXNlZnVsIHZpc3VhbGl6YXRpb24uCmBgYHtyfQpnK2dlb21fdmlvbGluKGFscGhhPTAuNSwgY29sb3I9ImdyYXkiKQpgYGAKCldoYXQgaWYgd2Ugcm90YXRlZCBhbmQgYWRkZWQgdGhlIGppdHRlcmVkIHBvaW50czoKYGBge3J9CmcrZ2VvbV92aW9saW4oYWxwaGE9MC41LCBjb2xvcj0iZ3JheSIpK2dlb21faml0dGVyKGFscGhhPTAuNSwgYWVzKGNvbG9yPXNlYXNvbiksIHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyKHdpZHRoPTAuMSksIHNpemU9MC42KStjb29yZF9mbGlwKCkKYGBgClRoaXMgaXMgbmljZSBhbmQgSSBsaWtlIGl0LiBCdXQgYmUgd2FyeSBvZiB1c2luZyB1bnVzdWFsIHBsb3QgdHlwZXMsIHRoZXkgdGFrZSBtb3JlIHRpbWUgZm9yIHlvdXIgdXNlcnMgdG8gdW5kZXJzdGFuZC4gU29tZXRpbWVzIHRoZSBzaW1wbGVzdCBhbmQgbW9zdCBjb252ZW50aW9uYWwgcGxvdCB0eXBlIGlzIHlvdXIgYmVzdCBiZXQgd2hlbiBzaGFyaW5nIHdpdGggb3RoZXJzLiBCb3ggcGxvdHMgbWF5IGJlIGJvcmluZyBidXQgcGVvcGxlIGtub3cgaG93IHRvIGludGVycHJldCB0aGVtIGltbWVkaWF0ZWx5LgoKIyMjIEFkZCBhIHJpYmJvbiB0byB5b3VyIHBsb3Q6IGdlb21fcmliYm9uKCkKVGhpcyBpcyBub3QgdGhlIHBlcmZlY3QgZGF0YXNldCBmb3IgdGhpcywgYnV0IHVzaW5nIHJpYmJvbiBjYW4gYmUgdXNlZnVsLiBJbiB0aGlzIGV4YW1wbGUgd2Ugd2lsbCBjcmVhdGUgYSAzMC1kYXkgcnVubmluZyBhdmVyYWdlIHVzaW5nIHRoZSBmaWx0ZXIoKSBmdW5jdGlvbiBzbyB0aGF0IG91ciByaWJib24gaXMgbm90IHRvbyBub2lzeS4KYGBge3J9CiMgQWRkIGEgZmlsdGVyCm5tbWFwcyRvM3J1bjwtYXMubnVtZXJpYyhmaWx0ZXIobm1tYXBzJG8zLCByZXAoMS8zMCwzMCksIHNpZGVzPTIpKQoKZ2dwbG90KG5tbWFwcywgYWVzKGRhdGUsIG8zcnVuKSkrZ2VvbV9saW5lKGNvbG9yPSJsaWdodHBpbms0IiwgbHdkPTEpCmBgYApOb3RlOiB3aGF0IGZpbHRlcigpIGRvZXMgd2l0aCBmaWx0ZXIgc2l6ZSA9IDMwIGlzIGV4cGxhaW5lZCBpbiBtb3JlIGRldGFpbHMgb24gdGhpcyBbU3RhY2tPdmVyZmxvdyB0aHJlYWRdKGh0dHA6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMTQzNzI4ODAvc2ltcGxlLWV4YW1wbGVzLW9mLWZpbHRlci1mdW5jdGlvbi1yZWN1cnNpdmUtb3B0aW9uLXNwZWNpZmljYWxseSkuIFNpbXBseSBwdXQsIHdlIGNyZWF0ZSBhIHJ1bm5pbmcgYXZlcmFnZSBhbmQgdXNlIGJvdGggc2lkZXMgb2YgdGhlIHZhbHVlIChzaWRlcz0yKSBpbnN0ZWFkIG9mIG9ubHkgdXNpbmcgcGFzdCB2YWx1ZXMgKHNpZGVzPTEpLiBTbyBvbiB0aGUgMTd0aCBBcHJpbCwgdGhlIG8zcnVuIHZhbHVlIHdvdWxkIGJlIHRoZSBydW5uaW5nIGF2ZXJhZ2Ugb2YgMXN0IEFwcmlsIHRvIDMwdGggQXByaWwuIElmIHNpZGVzPTEsIGl0IHdvdWxkIGluc3RlYWQgaGF2ZSBiZWVuIHRoZSBydW5uaW5nIGF2ZXJhZ2Ugb2YgMXN0IEFwcmlsIHRvIDE3dGggQXByaWwuIFNpZGVzIGNhbiBvbmx5IGJlIGVpdGhlciAxIG9yIDIuCkNvbnRyYXN0IHRoaXMgdG8gdGhlIG8zIHZhbHVlIG9mIDE3dGggQXByaWwgd2hpY2ggaXMganVzdCB0aGUgdmFsdWUgb2YgMTd0aCBBcHJpbCBhbG9uZS4gCgpIb3cgZG9lcyBpdCBsb29rIGlmIHdlIGZpbGwgaW4gdGhlIGFyZWEgYmVsb3cgdGhlIGN1cnZlIHVzaW5nIHRoZSBnZW9tX3JpYmJvbigpIGZ1bmN0aW9uPwpgYGB7cn0KZ2dwbG90KG5tbWFwcywgYWVzKGRhdGUsIG8zcnVuKSkgKyBnZW9tX3JpYmJvbihhZXMoeW1pbj0wLCB5bWF4PW8zcnVuKSwgZmlsbD0ibGlnaHRwaW5rMyIsIGNvbG9yPSJsaWdodHBpbmszIikgKyBnZW9tX2xpbmUoY29sb3I9ImxpZ2h0cGluazQiLCBsd2Q9MSkKYGBgCkZvciBlYWNoIHggdmFsdWUsIGdlb21fcmliYm9uIGRpc3BsYXlzIGEgeSBpbnRlcnZhbCBkZWZpbmVkIGJ5IHltaW4gYW5kIHltYXguICoqZ2VvbV9hcmVhKiogaXMgYSBzcGVjaWFsIGNhc2Ugb2YgZ2VvbV9yaWJib24sIHdoZXJlIHRoZSB5bWluIGlzIGZpeGVkIHRvIDAuCgpBYm92ZSBpcyBub3QgcmVhbGx5IHRoZSBjb252ZW50aW9uYWwgd2F5IHRvIHVzZSBnZW9tX3JpYmJvbigpLCB3ZSB3b3VsZCBoYXZlIHVzZWQgZ2VvbV9hcmVhIGluc3RlYWQuIEluc3RlYWQsIHdoeSBkb27igJl0IHdlIGRyYXcgYSByaWJib24gdGhhdCBnaXZlcyB1cyBvbmUgc3RhbmRhcmQgZGV2aWF0aW9uIGFib3ZlIGFuZCBiZWxvdyBvdXIgZGF0YToKYGBge3J9Cm5tbWFwcyRtaW5vMyA8LSBubW1hcHMkbzNydW4tc2Qobm1tYXBzJG8zcnVuLCBuYS5ybT1UKQpubW1hcHMkbWF4bzMgPC0gbm1tYXBzJG8zcnVuK3NkKG5tbWFwcyRvM3J1biwgbmEucm09VCkKCmdncGxvdChubW1hcHMsIGFlcyh4PWRhdGUsIHk9bzNydW4pKStnZW9tX3JpYmJvbihhZXMoeW1pbiA9IG1pbm8zLCB5bWF4ID0gbWF4bzMpLCBmaWxsPSJzdGVlbGJsdWUyIiwgY29sb3I9InN0ZWVsYmx1ZTIiKSArIGdlb21fbGluZShjb2xvcj0ic3RlZWxibHVlNCIsIGx3ZD0xKQpgYGAKCiMjIyBDcmVhdGUgYSB0aWxlZCBjb3JyZWxhdGlvbiBwbG90OiBnZW9tX3RpbGUoKQpGaXJzdCBzdGVwIGluIGNyZWF0aW5nIGEgdGlsZWQgY29ycmVsYXRpb24gcGxvdCBpcyB0byBjcmVhdGUgdGhlIGNvcnJlbGF0aW9uIG1hdHJpeC4gV2UgdXNlIFBlYXJzb24gYmVjYXVzZSBhbGwgdGhlIHZhcmlhYmxlcyBhcmUgZmFpcmx5IG5vcm1hbGx5IGRpc3RyaWJ1dGVkIGluIG91ciBubW1hcHMgZGF0YXNldC4gV2UgbWF5IHdhbnQgdG8gY29uc2lkZXIgU3BlYXJtYW4gaWYgb3VyIHZhcmlhYmxlcyBmb2xsb3cgYSBkaWZmZXJlbnQgcGF0dGVybi4gTm90ZSB0aGF0IHNpbmNlIGEgY29ycmVsYXRpb24gbWF0cml4IGhhcyByZWR1bmRhbnQgaW5mb3JtYXRpb24gd2Ugd2lsbCBiZSBzZXR0aW5nIGhhbGYgb2YgaXQgdG8gTkEuCgpgYGB7cn0KIyBjYXJlZnVsISBXZSdyZSBzb3J0aW5nIHRoZSBmaWVsZCBuYW1lcyBzbyB0aGF0IHRoZSBvcmRlcmluZyBpbiB0aGUgZmluYWwgcGxvdCBpcyBjb3JyZWN0LiBXZSBjYWxsIGNvcigpIHRvIGNyZWF0ZSB0aGUgY29ycmVsYXRpb24gbWF0cml4Cgp0aGVjb3IgPC0gcm91bmQoY29yKG5tbWFwc1ssc29ydChjKCJkZWF0aCIsICJ0ZW1wIiwgImRld3BvaW50IiwgInBtMTAiLCAibzMiKSldLAogICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJwZWFyc29uIiwgdXNlID0gInBhaXJ3aXNlLmNvbXBsZXRlLm9icyIpLDIpCnRoZWNvcltsb3dlci50cmkodGhlY29yKV0gPC0gTkEKdGhlY29yCmBgYApOb3cgd2Ugd2lsbCBwdXQgaXQgaW4gYSAibG9uZyIgZm9ybWF0IHVzaW5nIHRoZSBtZWx0IGZ1bmN0aW9uIGZyb20gdGhlIHJlc2hhcGUyIHBhY2thZ2UgYW5kIGRyb3AgdGhlIHJlY29yZHMgd2l0aCBOQSB2YWx1ZXMuIFB1dHRpbmcgaXQgaW4gYSBsb25nIGZvcm1hdCBtYWtlcyBpdCBwb3NzaWJsZSB0byBtYXAgdGhlIGFlc3RoZXRpYzogYWVzKFZhcjEsIFZhcjIpCgpgYGB7cn0KbGlicmFyeShyZXNoYXBlMikKdGhlY29yIDwtIG1lbHQodGhlY29yKQp0aGVjb3IkVmFyMTwtYXMuY2hhcmFjdGVyKHRoZWNvciRWYXIxKQp0aGVjb3IkVmFyMjwtYXMuY2hhcmFjdGVyKHRoZWNvciRWYXIyKQp0aGVjb3I8LW5hLm9taXQodGhlY29yKQpoZWFkKHRoZWNvcikKYGBgCldlIG9ubHkgcHJpbnRlZCB0aGUgaGVhZCBidXQgdGhlY29yIGFjdHVhbGx5IGhhcyAyNSByb3dzICg1IHZhcmlhYmxlcyBeMikKCk5vdyBmb3IgdGhlIHBsb3QuIFdlIGFyZSB1c2luZyBnZW9tX3RpbGUgYnV0IGlmIHlvdSBoYXZlIGEgbG90IG9mIGRhdGEgeW91IG1pZ2h0IGNvbnNpZGVyIGdlb21fcmFzdGVyIHdoaWNoIGNhbiBiZSBtdWNoIGZhc3Rlci4KYGBge3J9CmdncGxvdCh0aGVjb3IsIGFlcyhWYXIyLCBWYXIxKSkgKyBnZW9tX3RpbGUoZGF0YT10aGVjb3IsIGFlcyhmaWxsPXZhbHVlKSwgY29sb3I9IndoaXRlIikgKyBzY2FsZV9maWxsX2dyYWRpZW50Mihsb3c9ImJsdWUiLCBoaWdoPSJyZWQiLCBtaWQ9IndoaXRlIiwgbWlkcG9pbnQ9MCwgbGltaXQ9YygtMSwxKSwgbmFtZT0iQ29ycmVsYXRpb25cbihQZWFyc29uKSIpICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NDUsIHZqdXN0PTEsIHNpemU9OCwgaGp1c3Q9MSksIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChjb2xvcj0iZGFya3JlZCIsIHNpemU9OCwgZmFjZT0iYm9sZCIpKSArIGNvb3JkX2VxdWFsKCkKYGBgCgojIFdvcmtpbmcgd2l0aCBzbW9vdGhzCllvdSd2ZSBsaWtlbHkgYWxyZWFkeSBsZWFybmVkIGhvdyBhbWF6aW5nbHkgZWFzeSBpdCBpcyB0byBhZGQgYSBzbW9vdGggdG8geW91ciBkYXRhIHVzaW5nIGdncGxvdDIuIFlvdSBjYW4gc2ltcGx5IHVzZSBzdGF0X3Ntb290aCgpIHdoaWNoIHdpbGwgYWRkIGEgTE9FU1Mgc21vb3RoIGlmIHlvdSBoYXZlIGZld2VyIHRoYW4gMTAwMCBwb2ludHMgb3IgYSBHQU0gb3RoZXJ3aXNlLiBTaW5jZSB3ZSBoYXZlIG1vcmUgdGhhbiAxMDAwIHBvaW50cyB0aGUgc21vb3RoIGlzIGEgR0FNLgoKIyMjIERlZmF1bHQgLSBhZGRpbmcgTE9FU1Mgb3IgR0FNOiBzdGF0X3Ntb290aCgpCmBgYHtyfQojIFdpdGhvdXQgc3BlY2lmeWluZyBhbnkgZm9ybXVsYQpnZ3Bsb3Qobm1tYXBzLCBhZXMoZGF0ZSwgdGVtcCkpK2dlb21fcG9pbnQoY29sb3I9ImZpcmVicmljayIpK3N0YXRfc21vb3RoKCkKYGBgCgojIyMgU3BlY2lmeWluZyB0aGUgZm9ybXVsYTogc3RhdF9zbW9vdGgoZm9ybXVsYT0pCkJ1dCBnZ3Bsb3QyIGFsbG93cyB1cyB0byBzcGVjaWZ5IHRoZSBtb2RlbCB5b3Ugd2FudCBpdCB0byB1c2UuIEZvciBleGFtcGxlLCBsZXTigJlzIHNheSB3ZSB3YW50IHRvIGluY3JlYXNlIHRoZSBHQU0gZGltZW5zaW9uIChhZGQgc29tZSBhZGRpdGlvbmFsIHdpZ2dsZXMgdG8gdGhlIHNtb290aCk6CmBgYHtyfQpnZ3Bsb3Qobm1tYXBzLCBhZXMoZGF0ZSwgdGVtcCkpICsgZ2VvbV9wb2ludChjb2xvcj0iZ3JleSIpICsgICBzdGF0X3Ntb290aChtZXRob2Q9ImdhbSIsIGZvcm11bGEgPSB5fnMoeCwgaz0xMCksIGNvbD0iZGFya29saXZlZ3JlZW4yIiwgc2U9RkFMU0UsIHNpemU9MSkgKyBzdGF0X3Ntb290aChtZXRob2Q9ImdhbSIsIGZvcm11bGEgPSB5fnMoeCwgaz0zMCksIGNvbD0icmVkIiwgc2U9RkFMU0UsIHNpemU9MSkgKyBzdGF0X3Ntb290aChtZXRob2Q9ImdhbSIsIGZvcm11bGEgPSB5fnMoeCwgaz01MDApLCBjb2w9ImRvZGdlcmJsdWU0Iiwgc2U9RkFMU0UsIHNpemU9MSkKYGBgCgojIyMgQWRkaW5nIGEgbGluZWFyIGZpdDogc3RhdF9zbW9vdGgobWV0aG9kPSJsbSIpCkFsdGhvdWdoIHRoZSBkZWZhdWx0IGlzIHNtb290aCwgaXQgaXMgYWxzbyBlYXN5IHRvIGFkZCBhIHN0YW5kYXJkIGxpbmVhciBmaXQKYGBge3J9CiNzZT1UUlVFIGRpc3BsYXlzIGNvbmZpZGVuY2UgaW50ZXJ2YWwgYXJvdW5kIHNtb290aC4gRkFMU0UgdG8gb3ZlcnJpZGUgdGhpcyBkZWZhdWx0IGJlaGF2aW9yCmdncGxvdChubW1hcHMsIGFlcyh0ZW1wLCBkZWF0aCkpICsgZ2VvbV9wb2ludChjb2xvcj0iZmlyZWJyaWNrIikgKyBzdGF0X3Ntb290aChtZXRob2Q9ImxtIiwgc2U9RkFMU0UpCmBgYApOb3RlIHRoYXQgdGhlIHNhbWUgY291bGQgYmUgYWNoaWV2ZWQgdXNpbmcgdGhlIG1vcmUgY3VtYmVyc29tZToKYGBge3J9CmxtVGVtcCA8LSBsbShkZWF0aH50ZW1wLCBkYXRhPW5tbWFwcykKZ2dwbG90KG5tbWFwcywgYWVzKHRlbXAsIGRlYXRoKSkrZ2VvbV9wb2ludChjb2w9ImZpcmVicmljayIpK2dlb21fYWJsaW5lKGludGVyY2VwdCA9IGxtVGVtcCRjb2VmWzFdLCBzbG9wZSA9IGxtVGVtcCRjb2VmWzJdKQpgYGAKClRoZSBwcm9qZWN0IGlzIGFjY29tcGxpc2hlZCB1c2luZyBSIHZlcnNpb24gMy4yLjIgYW5kIGdncGxvdDIgdmVyc2lvbiAyLjIuMS4KYGBge3J9CnBhY2thZ2VWZXJzaW9uKCJnZ3Bsb3QyIikKcGFja2FnZVZlcnNpb24oImdndGhlbWVzIikKZ2V0UnZlcnNpb24oKQpgYGAKCgoKCg==